package com.wanxs.mall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wanxs.mall.common.service.impl.CrudServiceImpl;
import com.wanxs.mall.product.dao.CategoryDao;
import com.wanxs.mall.product.dto.CategoryDTO;
import com.wanxs.mall.product.entity.CategoryEntity;
import com.wanxs.mall.product.service.CategoryBrandRelationService;
import com.wanxs.mall.product.service.CategoryService;
import com.wanxs.mall.product.vo.Catalog2VO;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 商品三级分类
 *
 * @author wanxs 130001934@qq.com
 * @since 1.0.0 2023-09-01
 */
@Service
public class CategoryServiceImpl extends CrudServiceImpl<CategoryDao, CategoryEntity, CategoryDTO> implements CategoryService {


    @Autowired
    private CategoryBrandRelationService categoryBrandRelationService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public QueryWrapper<CategoryEntity> getWrapper(Map<String, Object> params){
        String id = (String)params.get("id");

        QueryWrapper<CategoryEntity> wrapper = new QueryWrapper<>();
        wrapper.eq(StringUtils.isNotBlank(id), "id", id);

        return wrapper;
    }

    @Override
    public List<CategoryEntity> queryPageWithTree(Map<String, Object> params){
        List<CategoryEntity> categoryEntities = baseDao.selectList(null);

        List<CategoryEntity> collect = categoryEntities.stream()
                .filter(categoryEntity -> {
                    boolean isParent = false;
                    if(categoryEntity.getParentCid() == 0){
                        isParent = true;
                        categoryEntity.setParentLevel(0);
                    }
                    return isParent;
                })
                .map(categoryEntity -> {
                    categoryEntity.setChildrens(getCategoryChildrens(categoryEntity, categoryEntities));
                    return categoryEntity;
                }).sorted((entity1, entity2) -> {
                    return entity1.getSort() == null ? 0 : entity1.getSort() - (entity2.getSort() == null ? 0 : entity2.getSort());
                }).collect(Collectors.toList());
        return collect;
    }

    private List<CategoryEntity> getCategoryChildrens(CategoryEntity categoryEntity, List<CategoryEntity> categoryEntities) {

        List<CategoryEntity> collect = categoryEntities.stream()
                .filter(entity -> {
                    boolean isParent = false;
                    if(entity.getParentCid().longValue() == categoryEntity.getCatId().longValue()){
                        isParent = true;
                        entity.setParentLevel(categoryEntity.getCatLevel());
                    }
                    return isParent;
                }
                ).map(entity -> {
                            entity.setChildrens(getCategoryChildrens(entity, categoryEntities));
                            return entity;
                        }
                ).sorted((entity1, entity2) -> {
                            return entity1.getSort() == null ? 0 : entity1.getSort() - (entity2.getSort() == null ? 0 : entity2.getSort());
                        }
                ).collect(Collectors.toList());
        return collect;
    }


    /**
     * 逻辑批量删除操作
     * @param ids
     */
    @Override
    public void removeCategoryByIds(List<Long> ids) {
        // TODO  1.检查类别数据是否在其他业务中使用

        // 2.批量逻辑删除操作
        baseDao.deleteBatchIds(ids);
        redissonClient.getKeys().delete(catelogjsonkey);

    }

    @Override
    public Long[] queryCatelogPath(Long catelogId) {
        Long[] result = new Long[3];

        CategoryEntity categoryEntity;

        for(int i = result.length - 1; i >= 0 ; i--){
            categoryEntity = baseDao.selectById(catelogId);
            if(categoryEntity == null ){
                break;
            }
            result[i] = categoryEntity.getCatId();
            catelogId = categoryEntity.getParentCid();
        }

        return result;
    }

    @Override
    public Long[] getCatelogPath(Long catelogId) {

        List<Long> pathList = new ArrayList<>();
        pathList = getParentCatId(pathList,catelogId);
        return pathList.toArray(new Long[]{});
    }

    @Transactional
    @Override
    public void updateDetail(CategoryEntity entity) {
        this.baseDao.updateById(entity);
        if(StringUtils.isNotEmpty(entity.getName())){
            categoryBrandRelationService.updateCategoryName(entity.getCatId(),entity.getName());
        }
        redissonClient.getKeys().delete(catelogjsonkey);
    }

    @Override
    public List<CategoryEntity> queryLevel1Categorys() {

        QueryWrapper<CategoryEntity> wrapper = new QueryWrapper<>();
        wrapper.eq( "parent_cid", 0);

        List<CategoryEntity> categoryEntities = baseDao.selectList(wrapper);

        return categoryEntities;
    }

    /**
     * 查询出所有的二级和三级分类的数据
     * 并封装为Map<String, Catalog2VO>对象
     * @return
     */
    @Override
    public Map<String, List<Catalog2VO>> getCatelog2JSON() {
        // 获取所有的一级分类的数据
        List<CategoryEntity> leve1Category = this.queryLevel1Categorys();
        // 把一级分类的数据转换为Map容器 key就是一级分类的编号， value就是一级分类对应的二级分类的数据
        Map<String, List<Catalog2VO>> map = leve1Category.stream().collect(Collectors.toMap(
                key -> key.getCatId().toString()
                , value -> {
                    // 根据一级分类的编号，查询出对应的二级分类的数据
                    List<CategoryEntity> l2Catalogs = baseDao
                            .selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", value.getCatId()));
                    List<Catalog2VO> Catalog2VOs =null;
                    if(l2Catalogs != null){
                        Catalog2VOs = l2Catalogs.stream().map(l2 -> {
                            // 需要把查询出来的二级分类的数据填充到对应的Catelog2VO中
                            Catalog2VO catalog2VO = new Catalog2VO(l2.getParentCid().toString(), null, l2.getCatId().toString(), l2.getName());
                            // 根据二级分类的数据找到对应的三级分类的信息
                            List<CategoryEntity> l3Catelogs = baseDao.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", catalog2VO.getId()));
                            if(l3Catelogs != null){
                                // 获取到的二级分类对应的三级分类的数据
                                List<Catalog2VO.Catalog3VO> catalog3VOS = l3Catelogs.stream().map(l3 -> {
                                    Catalog2VO.Catalog3VO catalog3VO = new Catalog2VO.Catalog3VO(l3.getParentCid().toString(), l3.getCatId().toString(), l3.getName());
                                    return catalog3VO;
                                }).collect(Collectors.toList());
                                // 三级分类关联二级分类
                                catalog2VO.setCatalog3List(catalog3VOS);
                            }
                            return catalog2VO;
                        }).collect(Collectors.toList());
                    }

                    return Catalog2VOs;
                }
        ));
        return map;
    }

    private List<Long> getParentCatId(List<Long> pathList, Long catelogId) {
        CategoryEntity categoryEntity = baseDao.selectById(catelogId);
        if(categoryEntity != null && categoryEntity.getParentCid() != 0){
            pathList = getParentCatId(pathList,categoryEntity.getParentCid());
        }
        pathList.add(categoryEntity.getCatId());
        return pathList;
    }
    /**
     * 跟进父编号获取对应的子菜单信息
     * @param list
     * @param parentCid
     * @return
     */
    private List<CategoryEntity> queryByParenCid(List<CategoryEntity> list,Long parentCid){
        List<CategoryEntity> collect = list.stream().filter(item -> {
            return item.getParentCid().equals(parentCid);
        }).collect(Collectors.toList());
        return collect;
    }

    private final static String catelogjsonkey = "catelogJSON";
    /**
     * 查询出所有的二级和三级分类的数据
     * 并封装为Map<String, Catalog2VO>对象
     * @return
     */
    @Override
    public Map<String, List<Catalog2VO>> getCatelog2JSON2() {
        String catelogJSON = stringRedisTemplate.opsForValue().get(catelogjsonkey);
        if(StringUtils.isEmpty(catelogJSON)){
            Map<String, List<Catalog2VO>> cagelogFromDB = getCagelogFromDB();
            if(cagelogFromDB == null){
                stringRedisTemplate.opsForValue().set(catelogjsonkey,"1",5, TimeUnit.SECONDS);
            }else {
                String jsonValue = JSON.toJSONString(cagelogFromDB);
                stringRedisTemplate.opsForValue().set(catelogjsonkey, jsonValue);
            }

        }
        Map<String, List<Catalog2VO>> stringListMap= JSON.parseObject(catelogJSON,new TypeReference< Map<String,List<Catalog2VO>>>(){});
        return stringListMap;
    }

    @Override
    public Map<String, List<Catalog2VO>> getCatelogJsonWithLock() throws InterruptedException {
        String catelogJSON = stringRedisTemplate.opsForValue().get(catelogjsonkey);
        if(StringUtils.isEmpty(catelogJSON)){
            RLock lock = redissonClient.getLock(catelogjsonkey);
            boolean islock = lock.tryLock(30, 30, TimeUnit.SECONDS);
            if(islock) {
                try {
                    Map<String, List<Catalog2VO>> cagelogFromDB = getCagelogFromDB();
                    if (cagelogFromDB == null) {
                        stringRedisTemplate.opsForValue().set(catelogjsonkey, "1", 5, TimeUnit.SECONDS);
                    } else {
                        String jsonValue = JSON.toJSONString(cagelogFromDB);
                        stringRedisTemplate.opsForValue().set(catelogjsonkey, jsonValue);
                    }
                }finally {
                    lock.unlock();
                }
            }
        }
        Map<String, List<Catalog2VO>> stringListMap= JSON.parseObject(catelogJSON,new TypeReference< Map<String,List<Catalog2VO>>>(){});
        return stringListMap;
    }

    private Map<String, List<Catalog2VO>> getCagelogFromDB() {
        // 获取所有的分类数据
        List<CategoryEntity> list  = baseDao.selectList(new QueryWrapper<CategoryEntity>());
        // 获取所有的一级分类的数据
        List<CategoryEntity> leve1Category = this.queryByParenCid(list,0l);
        // 把一级分类的数据转换为Map容器 key就是一级分类的编号， value就是一级分类对应的二级分类的数据
        Map<String, List<Catalog2VO>> map = leve1Category.stream().collect(Collectors.toMap(
                key -> key.getCatId().toString()
                , value -> {
                    // 根据一级分类的编号，查询出对应的二级分类的数据
                    List<CategoryEntity> l2Catalogs = this.queryByParenCid(list,value.getCatId());
                    List<Catalog2VO> Catalog2VOs =null;
                    if(l2Catalogs != null){
                        Catalog2VOs = l2Catalogs.stream().map(l2 -> {
                            // 需要把查询出来的二级分类的数据填充到对应的Catelog2VO中
                            Catalog2VO catalog2VO = new Catalog2VO(l2.getParentCid().toString(), null, l2.getCatId().toString(), l2.getName());
                            // 根据二级分类的数据找到对应的三级分类的信息
                            List<CategoryEntity> l3Catelogs = this.queryByParenCid(list,l2.getCatId());
                            if(l3Catelogs != null){
                                // 获取到的二级分类对应的三级分类的数据
                                List<Catalog2VO.Catalog3VO> catalog3VOS = l3Catelogs.stream().map(l3 -> {
                                    Catalog2VO.Catalog3VO catalog3VO = new Catalog2VO.Catalog3VO(l3.getParentCid().toString(), l3.getCatId().toString(), l3.getName());
                                    return catalog3VO;
                                }).collect(Collectors.toList());
                                // 三级分类关联二级分类
                                catalog2VO.setCatalog3List(catalog3VOS);
                            }
                            return catalog2VO;
                        }).collect(Collectors.toList());
                    }

                    return Catalog2VOs;
                }
        ));
        return map;
    }
}